#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>   // mmap 函数
 

 /*****
  *目的：  使用mmap映射gpio物理地址，实现拉gpio口功能
  * 
  *  relay_en = <&gpio3 RK_PA0 GPIO_ACTIVE_HIGH>;    GPIO3_A0     继电器  bit[0]
  * 
  *   通过   /dev/mem设备 操作 gpio 
  * 
  * 
  */
#define GPIO_REG_BASE 0xFE760000    //  GPIO3基地址 
#define GPIO_SWPORT_DDR_L_OFFSET 0x0008  //方向寄存器偏移量，低地址   0xFE760008      // io -r -4 0xFE760008     00002102       12101
#define GPIO_SWPORT_DR_L_OFFSET 0x0000   //数据寄存器偏移地址，低地址  0xFE760000      // io -r -4 0xFE760000    00002110       12110   bit[0] bit[16]  
#define SIZE_MAP 0x1000
 
// 打开LED灯
void LED_ON(unsigned char *base)
{
    // 设置LED灯的方向为输出
    *(volatile unsigned int *)(base + GPIO_SWPORT_DDR_L_OFFSET) = 0x12101;
    // 将LED灯打开
    *(volatile unsigned int *)(base + GPIO_SWPORT_DR_L_OFFSET) = 0x12111;
}
 
// 关闭LED灯
void LED_OFF(unsigned char *base)
{
    // 设置LED灯的方向为输出
    *(volatile unsigned int *)(base + GPIO_SWPORT_DDR_L_OFFSET) = 0x12101;
    // 将LED灯关闭
    *(volatile unsigned int *)(base + GPIO_SWPORT_DR_L_OFFSET) = 0x12110;
}
 
int main(int argc, char *argv[])
{
    int fd;
    unsigned char *map_base;
 
    // 打开/dev/mem设备 
    fd = open("/dev/mem", O_RDWR);
    if (fd < 0)
    {
        printf("open /dev/mem error \n");
        return -1;
    }
 
    // 将物理地址映射到用户空间
    /****
     *  参数1：指向要映射的起始地址，通过设置NULL，表示系统自选择地址
     *  参数2： 映射内存有多大  SIZE_MAP
     *   映射区域：   
     * 
     * 
     * 映射区域参数： 
     *  PROT_EXEC  Pages may be executed.     映射区域可被执行
        PROT_READ  Pages may be read.      映射区域可被读取
        PROT_WRITE Pages may be written.   映射区域可被写入 
        PROT_NONE  Pages may not be accessed.    映射区域不能被存取

flags: 由以下几个常值指定，MAP_SHARED，MAP_PRIVATE，MAP_FIXED，其中MAP_SHARED，MAP_PRIVATE必选其一，MAP_FIXED不推荐使用


fd ： 要映射控件的内存描述符

GPIO_REG_BASE ： 文件映射的偏移量 ，我们设置寄存器基地址

 内核都是虚拟地址
     * 
     */
    map_base = (unsigned char *)mmap(NULL, SIZE_MAP, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO_REG_BASE);
    if (map_base == MAP_FAILED)
    {
        printf("map_base error \n");
        return -2;
    }
 
    while (1)
    {
        // 打开LED灯
        LED_ON(map_base);
        // 等待1秒
        usleep(1000000);
        // 关闭LED灯
        LED_OFF(map_base);
        // 等待1秒
        usleep(1000000);
    }
 
    // 解除映射
    munmap(map_base, SIZE_MAP);
 
    // 关闭文件描述符
    close(fd);
 
    return 0; // 返回0表示程序正常退出
}